﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Vibz.TeamAssignment.Common.Entity;
using Extensil.Common;

namespace Vibz.TeamAssignment
{
    public enum ImportColumns
    {
        None,
        Title,
        Description,
        DueDate,
        TaskGroup,
        ParentTask,
        AssignedTo,
        EstimatedDuration
    }
    internal partial class TaskImportExcel :Form
    {
        BackgroundWorker bwLoader;
        DataTable _dataList;
        public AssignmentClient _aClient;
        public TaskImportExcel(AssignmentClient aClient)
        {
            InitializeComponent();
            btnImport.Enabled = false;
            _aClient = aClient;
            dgvImport.DataSourceChanged += new EventHandler(dgvImport_DataSourceChanged);
        }

        private void btnBrowse_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = "Excel files (*.xls or *.xlsx)|*.xls;*.xlsx";
            openFileDialog1.InitialDirectory = @"C:\";
            openFileDialog1.Title = "Please select excel file to import.";

            if(this.openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                dgvImport.Columns.Clear();
                _dataList = Excel.Reader.ExcelToDataTable(openFileDialog1.FileName);
                DataTable mapTable = new DataTable();
                mapTable.Columns.Add("Excel Column");
                mapTable.Columns.Add("MappedColumn");
                foreach(DataColumn col in _dataList.Columns)
                {
                    ImportColumns mapColumn = ImportColumns.None;
                    switch(col.ColumnName)
                    {
                        case "Title":
                        case "Name":
                            mapColumn = ImportColumns.Title;
                            break;
                        case "Desc":
                        case "Description":
                        case "Information":
                            mapColumn = ImportColumns.Description;
                            break;
                        case "Due Date":
                        case "DueDate":
                        case "End Date":
                            mapColumn = ImportColumns.DueDate;
                            break;
                        case "Task Group":
                        case "TaskGroup":
                        case "Group":
                            mapColumn = ImportColumns.TaskGroup;
                            break;
                        case "AssignedTo":
                        case "Assigned To":
                        case "AssignTo":
                        case "Assign To":
                        case "Assigned":
                        case "Assign":
                            mapColumn = ImportColumns.AssignedTo;
                            break;
                        case "ParentTask":
                        case "Parent Task":
                        case "Parent":
                            mapColumn = ImportColumns.ParentTask;
                            break;
                    }
                    mapTable.Rows.Add(col.ColumnName, mapColumn);
                }
                var column = new DataGridViewComboBoxColumn();
                column.Name = "Mapped Column";
                column.DataSource = Enum.GetNames(typeof(ImportColumns));
                dgvImport.Columns.Add(column);
                dgvImport.DataSource = mapTable;
            }
        }

        void dgvImport_DataSourceChanged(object sender, EventArgs e)
        {
            dgvImport.Columns["MappedColumn"].Visible = false;
            dgvImport.Columns["Mapped Column"].DisplayIndex = 2;
            foreach(DataGridViewRow dr in dgvImport.Rows)
            {
                ((DataGridViewComboBoxCell)dr.Cells["Mapped Column"]).Value = ((DataGridViewCell)dr.Cells["MappedColumn"]).Value;
            }
            string message;
            if(!ValidateDataList(out message))
            {
                AlertBox.ShowDialog(message);
                return;
            }
        }

        
        private void btnValidate_Click(object sender, EventArgs e)
        {
            string message;
            if(!ValidateDataList(out message))
                AlertBox.ShowDialog(message.Trim());
            else
                btnImport.Enabled = true;
        }
        bool ValidateDataList(out string message)
        {
            message = "";
            Dictionary<ImportColumns, string> colMap = new Dictionary<ImportColumns, string>();
            foreach(DataGridViewRow dr in dgvImport.Rows)
            {
                if(((DataGridViewCell)dr.Cells["MappedColumn"]).Value != null 
                    && ((DataGridViewComboBoxCell)dr.Cells["Mapped Column"]).Value != null)
                {
                    ImportColumns vC = (ImportColumns)Enum.Parse(typeof(ImportColumns), ((DataGridViewComboBoxCell)dr.Cells["Mapped Column"]).Value.ToString());
                    if(vC == ImportColumns.None)
                        continue;
                    if(colMap.ContainsKey(vC))
                    {
                        AlertBox.ShowDialog("Duplicate column '" + vC.ToString() + "'");
                        return false;
                    }
                    colMap.Add(vC, ((DataGridViewCell)dr.Cells["Excel Column"]).Value.ToString());
                }
            }

            if(!colMap.ContainsKey(ImportColumns.Title))
            {
                message += "\r\nTitle can not be mapped.";
            }
            DataTable taskList = new DataTable();
            foreach(ImportColumns cols in colMap.Keys)
            {
                taskList.Columns.Add(cols.ToString());
            }
            List<string> tasks = new List<string>();
            foreach(DataRow dr in _dataList.Rows)
            {
                DataRow drNew = taskList.NewRow();
                foreach(DataColumn dc in taskList.Columns)
                {
                    ImportColumns colType = (ImportColumns)Enum.Parse(typeof(ImportColumns), dc.ColumnName);
                    drNew[dc.ColumnName] = dr[colMap[colType]];
                    if(colMap.ContainsKey(ImportColumns.Title))
                        tasks.Add(drNew[ImportColumns.Title.ToString()].ToString());
                    switch(colType)
                    {
                        case ImportColumns.TaskGroup:
                            if(!String.IsNullOrEmpty(drNew[dc.ColumnName].ToString().Trim())
                                && _aClient.GetTaskGroupID(drNew[dc.ColumnName].ToString()) == -1)
                            {
                                message += "\r\n" + colMap[colType] + " at Row index " + _dataList.Rows.IndexOf(dr) + " is invalid. " + drNew[dc.ColumnName].ToString() + " is not a recognised task group.";
                                break;
                            }
                            break;
                        case ImportColumns.AssignedTo:
                            if(!String.IsNullOrEmpty(drNew[dc.ColumnName].ToString().Trim())
                                && _aClient.GetAllTeamMember().Where(u => u.MemberID.ToLower() == drNew[dc.ColumnName].ToString().ToLower()).Count() == 0)
                            {
                                message += "\r\n" + colMap[colType] + " at Row index " + _dataList.Rows.IndexOf(dr) + " is invalid. " + drNew[dc.ColumnName].ToString() + " is not a recognised team member.";
                                break;
                            }
                            break;
                        case ImportColumns.ParentTask:
                            if(colMap.ContainsKey(ImportColumns.Title))
                            {
                                string parentTask = drNew[dc.ColumnName].ToString();
                                if(!String.IsNullOrEmpty(drNew[dc.ColumnName].ToString().Trim())
                                    && !tasks.Contains(drNew[dc.ColumnName].ToString()))
                                {
                                    Task task = _aClient.GetTask(drNew[dc.ColumnName].ToString());
                                    if(task == null)
                                        message += "\r\n" + colMap[colType] + " at Row index " + _dataList.Rows.IndexOf(dr) + " is invalid. " + drNew[dc.ColumnName].ToString() + " is not recognized task. Make sure the parent task is positioned up in the Excel list if task and its parent are present in the same Excel.";
                                    break;
                                }
                                drNew[dc.ColumnName] = parentTask;
                            }
                            break;
                        case ImportColumns.EstimatedDuration:
                            double estDur = 0;
                            if(!String.IsNullOrEmpty(drNew[dc.ColumnName].ToString().Trim())
                                && !double.TryParse(drNew[dc.ColumnName].ToString(), out estDur))
                            {
                                message += "\r\n" + colMap[colType] + " at Row index " + _dataList.Rows.IndexOf(dr) + " is invalid.";
                                break;
                            }
                            drNew[dc.ColumnName] = estDur;
                            break;
                        case ImportColumns.DueDate:
                            DateTime dtDueDate = DateTime.Today;
                            if(!DateTime.TryParse(ConvertToDateTime(drNew[dc.ColumnName].ToString()), out dtDueDate))
                            {
                                message += "\r\n" + colMap[colType] + " at Row index " + _dataList.Rows.IndexOf(dr) + " is invalid.";
                                break;
                            }

                            if(dtDueDate.Date.Subtract(DateTime.Today).Days < 0)
                            {
                                message += "\r\n" + colMap[colType] + " at Row index " + _dataList.Rows.IndexOf(dr) + " is invalid. Due date of a task can not be less than today.";
                                break;
                            }

                            drNew[dc.ColumnName] = dtDueDate;        

                            //if(_parentTask != null && dtpDueDate.Value.Date.Subtract(_parentTask.DueDate.Date).Days > 0)
                            //    throw new Exception("Due date of a task can not be greater than due date of its Parent task.");
                            break;
                    }
                }
                bool tgPresent = _dataList.Columns.Contains(ImportColumns.TaskGroup.ToString());
                bool atPresent = _dataList.Columns.Contains(ImportColumns.AssignedTo.ToString());
                int tgId = -1;
                if(tgPresent)
                    tgId = _aClient.GetTaskGroupID(drNew[ImportColumns.TaskGroup.ToString()].ToString());
                string assTo = "";
                if(atPresent)
                    assTo = drNew[ImportColumns.AssignedTo.ToString()].ToString();
                if((tgPresent && atPresent && tgId == -1 && !String.IsNullOrEmpty(assTo)) 
                    || (!tgPresent && atPresent && !String.IsNullOrEmpty(assTo)))
                    message += "\r\nAt Row index " + _dataList.Rows.IndexOf(dr) + ", assigned user can only be specified to non-personal task. Define a valid Task Group.";

                taskList.Rows.Add(drNew);
            }

            dgvlistData.DataSource = taskList;
            return String.IsNullOrEmpty(message.Trim());
        }
        
        string ConvertToDateTime(string strExcelDate)
        {
            double excelDate;
            try
            {
                excelDate = Convert.ToDouble(strExcelDate);
            }
            catch
            {
                try
                {
                    return Convert.ToDateTime(strExcelDate).ToShortDateString();
                }
                catch
                {
                    return strExcelDate;
                }
            }
            if(excelDate < 1)
            {
                throw new ArgumentException("Excel dates cannot be smaller than 0.");
            }
            DateTime dateOfReference = new DateTime(1900, 1, 1);
            if(excelDate > 60d)
            {
                excelDate = excelDate - 2;
            }
            else
            {
                excelDate = excelDate - 1;
            }
            return dateOfReference.AddDays(excelDate).ToShortDateString();
        }
        private void btnImport_Click(object sender, EventArgs e)
        {
            createdTaskList = new Dictionary<string, int>();
            DataTable taskList = (DataTable)dgvlistData.DataSource;
            foreach(DataRow dr in taskList.Rows)
            {
                string assignTo=AssignmentClient.CurrentUser;
                string parentTask = "";
                Task tsk = new Task(-1, "", "", DateTime.Today, 1, -1);
                foreach(DataColumn dc in taskList.Columns)
                {
                    switch((ImportColumns)Enum.Parse(typeof(ImportColumns), dc.ColumnName))
                    {
                        case ImportColumns.Description:
                            tsk.TaskDescription = dr[dc.ColumnName].ToString();
                            break;
                        case ImportColumns.DueDate:
                            DateTime dt = DateTime.Today;
                            DateTime.TryParse(ConvertToDateTime(dr[dc.ColumnName].ToString()), out dt);
                            tsk.DueDate = dt;
                            break;
                        case ImportColumns.EstimatedDuration:
                            double est = 0;
                            double.TryParse(dr[dc.ColumnName].ToString(), out est);
                            tsk.EstimatedDuration = est;
                            break;
                        case ImportColumns.Title:
                            tsk.Title = dr[dc.ColumnName].ToString();
                            lstStatus.Items.Add("Creating task " + tsk.Title);
                            break;
                        case ImportColumns.TaskGroup:
                            tsk.GroupID = _aClient.GetTaskGroupID(dr[dc.ColumnName].ToString());
                            break;
                        case ImportColumns.AssignedTo:
                            assignTo = dr[dc.ColumnName].ToString();
                            break;
                        case ImportColumns.ParentTask:
                            parentTask = dr[dc.ColumnName].ToString();
                            break;
                    }
                }
                if(!String.IsNullOrEmpty(parentTask) && createdTaskList.ContainsKey(parentTask))
                    tsk.ParentID = createdTaskList[parentTask];
                else if(!String.IsNullOrEmpty(parentTask))
                {
                    Task task = _aClient.GetTask(parentTask);
                    if(task != null)
                        tsk.ParentID = task.TaskID;
                }

                if(CreateTask(tsk, assignTo))
                    lstStatus.Items.Add("Task " + tsk.Title + " created.");
            }
        }

        internal bool CreateTask(Task tsk, string assignedTo)
        {
            try
            {
                string assignTo = assignedTo;
                string assignStatus = AssignStatus.Assigned.ToString();

                CreateUpdateTask(tsk, assignTo, "", assignStatus);
                return true;
            }
            catch(Exception exc)
            {
                AlertBox.ShowDialog(exc.Message);
                return false;
            }
        }
        Dictionary<string, int> createdTaskList = new Dictionary<string, int>();
        void CreateUpdateTask(Task task, string assignTo, string assignComment, string assignStatus)
        {
            int taskID = _aClient.CreateTaskAssignment(task, null, null, assignTo, assignComment, assignStatus);
            if(!createdTaskList.ContainsKey(task.Title))
                createdTaskList.Add(task.Title, taskID);
        }

    }
}
